﻿#ifndef _CELL_SELECT_SERVER_H_
#define _CELL_SELECT_SERVER_H_

#include "CELLServer.hpp"

class CELLSelectServer : public CELLServer
{
public:
	CELLSelectServer() {}

	virtual ~CELLSelectServer()
	{
		Close();
	}

	virtual bool DoNetEvents() override
	{
		// fd_set fdException;  // 异常集合
	// 计算可读集合
		if (_clients_change)
		{
			_clients_change = false;
			// 清理读集合
			FD_ZERO(&_fdRead);
			for (auto pClient : _clients)
			{
				SOCKET tmpsock = pClient.first;
				_maxSock = _maxSock < tmpsock ? tmpsock : _maxSock;
				FD_SET(tmpsock, &_fdRead);
			}
			memcpy(&_fdRead_bak, &_fdRead, sizeof(fd_set));
		}
		else
		{
			memcpy(&_fdRead, &_fdRead_bak, sizeof(fd_set));
		}

		// 计算可写集合
		bool bNeedWrite = false;
		FD_ZERO(&_fdWrite);
		// 检测所有客户端有数据发送，才加入可写检测
		for (auto pClient : _clients)
		{
			if (pClient.second->needWrite())
			{
				bNeedWrite = true;
				FD_SET(pClient.second->sockfd(), &_fdWrite);
			}
		}

		// memcpy(&fdException, &_fdRead_bak, sizeof(fd_set));

		time_t time = _time_events.getTime();
		timeval ti{ 0, time * 1000 };

		int ret = 0;
		if (bNeedWrite)
		{
			if (time <= 0)
				ret = select(_maxSock + 1, &_fdRead, &_fdWrite, nullptr, nullptr);
			else
				ret = select(_maxSock + 1, &_fdRead, &_fdWrite, nullptr, &ti);
		}
		else
		{
			if (time <= 0)
				ret = select(_maxSock + 1, &_fdRead, nullptr, nullptr, nullptr);
			else
				ret = select(_maxSock + 1, &_fdRead, nullptr, nullptr, &ti);
		}

		do {
			if (ret < 0)
			{
				if (errno == EINTR)
				{
					break;
				}

				CELLLog_pError("CELLServer %d select error exit", _id);
				_time_events.process_timer();
				return false;
			}
			else if (ret == 0)
			{
				// 没有可读或可写就跳过
				break;
			}

			ReadData();
			WriteData();
			// 如果查询异常有，那么当作一次可写
			// WriteData(fdException);
			// CELLLog_Info("CELLServer OnRun select: fdR=%d, fdW=%d", fdRead.fd_count, fdWrite.fd_count);
			/*if (fdException.fd_count > 0)
			{
				CELLLog_Info("###fdExcepton=%d", fdException.fd_count);
			}*/
		} while (0);
		_time_events.process_timer();
		return true;
	}

	// 处理DoNetEvents函数返回后的客户端发送的消息
	void ReadData()
	{
		// 在循环中不改变迭代器指针，在循环中会删除元素，在这之间进行改变
		for (auto iter = _clients.begin(); iter != _clients.end(); )
		{
			// 客户端是否有数据发送过来
			if (FD_ISSET(iter->first, &_fdRead))
			{
				if (RecvData(iter->second) == SOCKET_ERROR)
				{
					OnClientLeave(iter->second);
					// 在这里进行迭代器指针的自增(后置++，先赋值再自增)
					auto iterOld = iter++;
					_clients.erase(iterOld);
					continue;
				}
			}
			// 在这里进行迭代器指针的自增
			++iter;
		}
	}

	// 处理DoNetEvents函数返回后的可写的客户端
	void WriteData()
	{
		// 在循环中不改变迭代器指针，在循环中会删除元素，在这之间进行改变
		for (auto iter = _clients.begin(); iter != _clients.end(); )
		{
			// 客户端是否需要写和有数据发送过来
			if (iter->second->needWrite() && FD_ISSET(iter->first, &_fdWrite))
			{
				if (SOCKET_ERROR == iter->second->SendDataReal())
				{
					OnClientLeave(iter->second);
					// 在这里进行迭代器指针的自增(后置++，先赋值再自增)
					auto iterOld = iter++;
					_clients.erase(iterOld);
					continue;
				}
			}
			// 在这里进行迭代器指针的自增
			++iter;
		}
	}
private:
	// 备份客户端集合
	fd_set _fdRead_bak;
	// 读集合
	fd_set _fdRead;
	// 写集合
	fd_set _fdWrite;
	// 用于select函数参数的当前最大套接字
	SOCKET _maxSock = 0;
};

#endif // #ifndef _CELL_SELECT_SERVER_H_
